package com.bachelor.community.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.bachelor.community.mapper.QuestionMapper;
import com.bachelor.community.mapper.TagMapper;
import com.bachelor.community.mapper.UserMapper;
import com.bachelor.community.model.dto.QuestionDTO;
import com.bachelor.community.model.entity.*;
import com.bachelor.community.model.vo.QuestionVO;
import com.bachelor.community.service.intf.AnswerService;
import com.bachelor.community.service.intf.QuestionService;
import com.bachelor.community.service.intf.QuestionTagService;
import com.bachelor.community.service.intf.TagService;
import com.bachelor.community.utils.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Resource
    private final QuestionMapper questionMapper;
    @Resource
    private final QuestionTagService questionTagService;
    @Resource
    private final TagMapper tagMapper;
    @Resource
    private final UserMapper userMapper;
    @Resource
    private final TagService tagService;
    @Resource
    private AnswerService answerService;

    public QuestionServiceImpl(QuestionMapper questionMapper, QuestionTagService questionTagService, TagMapper tagMapper, UserMapper userMapper, TagService tagService) {
        this.questionMapper = questionMapper;
        this.questionTagService = questionTagService;
        this.tagMapper = tagMapper;
        this.userMapper = userMapper;
        this.tagService = tagService;
    }

    @Override
    public IPage<QuestionVO> listQuestions(int currentPage, int pageSize, String orderColumn, String keyword) {
        QueryWrapper<QuestionVO> queryWrapper = new QueryWrapper<>(); // 此处无法再用lambdaQueryWrapper, 会报缓存错误
        queryWrapper.eq("state", 1);
        queryWrapper.orderByDesc("is_essential");
        if (orderColumn.equals("hot")) {
            queryWrapper.orderByDesc("view_count");
        } else if (orderColumn.equals("latest")) {
            queryWrapper.orderByDesc("create_time");
        } else {
            queryWrapper.orderByDesc("create_time");
            queryWrapper.eq("answer_count", 0);
        }
        IPage<QuestionVO> page = new Page<>(currentPage, pageSize);
        if (StrUtil.isBlank(keyword)) {
            questionMapper.listQuestionVO(page, queryWrapper); // 这句话得放到前面
        } else {
            questionMapper.listQuestionsByKeyword(page, queryWrapper, keyword);
        }
        page.getRecords().forEach(this::findTagsByQuestionIds);
        return page;
    }

    @Override
    public IPage<QuestionVO> listQuestions(int currentPage, int pageSize, QueryWrapper<QuestionVO> queryWrapper) {
        queryWrapper.orderByDesc("is_top");
        queryWrapper.orderByDesc("create_time");
        IPage<QuestionVO> page = new Page<>(currentPage, pageSize);
        questionMapper.listQuestionVO(page, queryWrapper); // 这句话得放到前面
        page.getRecords().forEach(this::findTagsByQuestionIds);
        return page;
    }

    @Override
    public IPage<QuestionVO> listQuestionsByUserId(Integer currentPage, Integer pageSize, Integer userId) {
        QueryWrapper<QuestionVO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("question.creator", userId);
        queryWrapper.orderByDesc("create_time");
        IPage<QuestionVO> page = new Page<>(currentPage, pageSize);
        questionMapper.listQuestionVO(page, queryWrapper); // 这句话得放到前面
        page.getRecords().forEach(this::findTagsByQuestionIds);
        return page;
    }

    @Override
    public Question insertQuestion(QuestionDTO questionDTO, User user) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("title", questionDTO.getTitle());
        Question question;
        try {
            question = getOne(queryWrapper);
        } catch (Exception e) {
            throw new ServiceException("500", "服务器故障，请联系管理员");
        }
        if (ObjectUtil.isEmpty(question)) {
            question = Question.builder()
                    .title(questionDTO.getTitle())
                    .content(questionDTO.getContent())
                    .creator(user.getId())
                    .reward(questionDTO.getReward())
                    .executionResult(questionDTO.getExecutionResult())
                    .errorMessage(questionDTO.getErrorMessage())
                    .completeCode(questionDTO.getCompleteCode())
                    .errorCode(questionDTO.getErrorCode())
                    .errorRow(questionDTO.getErrorRow())
                    .errorColumn(questionDTO.getErrorColumn())
                    .createTime(DateUtil.date().toTimestamp()).build();
            save(question);
            int newScore = user.getCredit() + 1 - questionDTO.getReward();
            userMapper.updateById(user.setCredit(newScore));
            List<String> tagsTemp = questionDTO.getTags();
            if (!ObjectUtil.isEmpty(tagsTemp)) {
                List<Tag> tags = tagService.insertTags(tagsTemp);
                questionTagService.createQuestionTag(question.getId(), tags);
            }
        } else {
            throw new ServiceException("422", "该问题已存在，请修改");
        }
        return question;
    }

    @Override
    public QuestionVO getQuestionVOById(Long id) {
        Question question = questionMapper.selectById(id);
        question.setViewCount(question.getViewCount() + 1);
        updateById(question);
        QuestionVO questionVO = questionMapper.getQuestionVO(id);
        findTagsByQuestionIds(questionVO);
        return questionVO;
    }

    @Override
    public IPage<QuestionVO> listQuestionsByTagId(Integer currentPage, Integer pageSize, Integer tagId, String tabName) {
        Set<Long> ids = questionTagService.listQuestionIdsByTagId(tagId);
        Tag tag = tagService.getById(tagId);
        tag.setCount(ids.size());
        tagService.updateById(tag);
        QueryWrapper<QuestionVO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("state", 1);
        queryWrapper.in("question.id", ids);
        if (tabName.equals("latest")) {
            queryWrapper.orderByDesc("update_time");
            queryWrapper.orderByDesc("create_time");
        } else if (tabName.equals("hot")) {
            queryWrapper.orderByDesc("view_count");
        } else {
            queryWrapper.eq("answer_count", 0);
        }
        IPage<QuestionVO> page = new Page<>(currentPage, pageSize);
        questionMapper.listQuestionVO(page, queryWrapper); // 这句话得放到前面
        page.getRecords().forEach(this::findTagsByQuestionIds);
        return page;
    }

    @Override
    public QuestionVO partialUpdateQuestion(Long id, QuestionDTO questionDTO) {
        Question question = getById(id);
        if (ObjectUtil.isEmpty(questionDTO.getTitle())) {
            question.setEssential(questionDTO.getEssential());
            question.setTop(questionDTO.getTop());
        } else {
            question.setContent(questionDTO.getContent());
            question.setTitle(questionDTO.getTitle());
            question.setUpdateTime(DateUtil.date().toTimestamp());
            question.setExecutionResult(questionDTO.getExecutionResult());
            question.setErrorMessage(questionDTO.getErrorMessage());
            question.setCompleteCode(questionDTO.getCompleteCode());
            question.setErrorCode(questionDTO.getErrorCode());
            question.setErrorRow(questionDTO.getErrorRow());
            question.setErrorColumn(questionDTO.getErrorColumn());
        }
        question.setSolved(questionDTO.getIsSolved());
        question.setState(questionDTO.getState());
        updateById(question);
        QuestionVO questionVO = new QuestionVO();
        BeanUtil.copyProperties(question, questionVO, true);
        return questionVO;
    }

    private void findTagsByQuestionIds(QuestionVO questionVO) {
        QueryWrapper<Answer> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("question_id", questionVO.getId());
        Integer answerCount = Convert.toInt(answerService.count(queryWrapper));
        questionVO.setAnswerCount(answerCount);
        List<QuestionTagRelation> questionTags = questionTagService.selectByQuestionId(questionVO.getId());
        if (!questionTags.isEmpty()) {
            List<Integer> questionIds = questionTags.stream().map(QuestionTagRelation::getTagId).collect(Collectors.toList());
            List<Tag> tags = tagMapper.selectBatchIds(questionIds);
            questionVO.setTags(tags);
        }
    }
}
